package mskruch.gps;

import mskruch.gps.calculation.Calculations;

/**
 * Calculations on positions
 * 
 * @author Marcin Skruch
 */
public class Positions {
    private Position[] data;

    /** calculated by {@link Positions#calculateBoundaries()} */
    private Double minimumLongitude;
    /** calculated by {@link Positions#calculateBoundaries()} */
    private Double minimumLatitude;
    /** calculated by {@link Positions#calculateBoundaries()} */
    private Double maximumLongitude;
    /** calculated by {@link Positions#calculateBoundaries()} */
    private Double maximumLatitude;
    /** calculated by {@link Positions#calculateDistance()} */
    private Double distance;

    public Positions(Position[] data) {
        super();
        if (data == null || data.length == 0) {
            throw new IllegalArgumentException("data can't be empty");
        }
        this.data = data;
    }

    private void calculateBoundaries() {
        Position first = data[0];

        double maxLon = first.getLongitude();
        double maxLat = first.getLatitude();
        double minLon = first.getLongitude();
        double minLat = first.getLatitude();

        for (Position p : data) {
            final double lon = p.getLongitude();
            final double lat = p.getLatitude();
            if (lon > maxLon) {
                maxLon = lon;
            } else if (lon < minLon) {
                minLon = lon;
            }
            if (lat > maxLat) {
                maxLat = lat;
            } else if (lat < minLat) {
                minLat = lat;
            }
        }

        this.minimumLongitude = minLon;
        this.minimumLatitude = minLat;
        this.maximumLongitude = maxLon;
        this.maximumLatitude = maxLat;
    }

    private void calculateDistance() {
        this.distance = Calculations.newPositionsCalculator().add(data).getDistance();
    }

    public Double getMinimumLongitude() {
        if (minimumLongitude == null) {
            calculateBoundaries();
        }
        return minimumLongitude;
    }

    public Double getMinimumLatitude() {
        if (minimumLatitude == null) {
            calculateBoundaries();
        }
        return minimumLatitude;
    }

    public Double getMaximumLongitude() {
        if (maximumLongitude == null) {
            calculateBoundaries();
        }
        return maximumLongitude;
    }

    public Double getMaximumLatitude() {
        if (maximumLatitude == null) {
            calculateBoundaries();
        }
        return maximumLatitude;
    }

    public Double getDistance() {
        if (distance == null) {
            calculateDistance();
        }
        return distance;
    }

    /**
     * Finds checkpoints in given track (points)
     * 
     * @param number
     *            number of checkpoints to find
     * @return checkpoints
     */
    public Position[] findCheckpoints(int number) {
        double totalDistance = getDistance();
        Position[] checkPoints = new Position[number];
        double[] checkDistances = new double[number];
        for (int i = 0; i < checkDistances.length; i++) {
            checkDistances[i] = ((double) (i + 1)) * totalDistance / (number + 1);
        }

        int index = 0; /* checkDistances */
        double dist = 0;
        for (int i = 1; i < data.length; i++) {
            final Position prev = data[i - 1];
            final Position curr = data[i];

            dist += curr.distanceTo(prev);
            if (index < number && dist >= checkDistances[index]) {
                checkPoints[index] = curr;
                index++;
            }
        }
        return checkPoints;

    }

}
